Xbasic Features

Description

Alpha Anywhere now supports GUIDs as an intrinsic Xbasic data type (designated as type 'K'), just as Character, Numeric, Logical, Time, etc. are intrinsic Xbasic data types. Support for GUID data types is important for Active-Link tables as some SQL databases use GUIDS for primary keys. You can DIM a variable as a GUID, (e.g. DIM varname as K), or you can use the curly bracket notation to specify a value of type 'K' (just as curly bracket notation is used to specify a Date value). The following Interactive window session shows examples:

GUID Data Type

'DIM a variable as a GUID
 dim g as k 

'Use the built-in Function to create a GUID value
 g = api_uuidcreate()
 ?typeof(g)
 = "K"
  

'Use curly bracket notation to create a GUID value
 g2 = {f5ab3018-496a-419f-af71-6c19fec1252b}
 ?typeof(g2)
 = "K"
  

g2 = {clearly-not-a-valid-guid}

ERROR: Constant operator is not recognized

Dependency Mapper

The dependency mapper allows you to keep track of a collection of variables and easily determine which, if any, of the watched variables have changed. You create a dependency mapper from a CR-LF delimited list of variables or expressions to watch. At any time, you can call the dependency mapper's .Changes() function to get a list of variables that have changed since the last call to .Changes(). You can control what is returned when a variable has changed by defining a string of text (which can be code to execute). The following Interactive window session shows an example:

dim lastname as c 
lastname = "smith"
dim firstname as c 
firstname = "john"
dim dependencyList as c 

'The format of the dependency list is a crlf delimited list of variables or expressions to watch, 
'followed by an exclamation character and then an expression that is executed.

dependencyList = "lastname!'lastname_changed'" + crlf() + "firstname!'firstname_changed'"
dm = dependency.create(dependencyList)

'Create a client based on the dependency mapper that has been instantiated
dc = dm.client_create()
?dc.changes()
= lastname_changed
firstname_changed

'Now call dc.changes() a second time
'Note that this time nothing is returned because the variables have not been
'changed since we last called the .changes() method.
?dc.changes() 
= ""

'Now change lastname
lastname = "jones"
?dc.changes()
= lastname_changed

'In the above example, the .changes() method is returning either 'lastname_changed', or 'firstname_changed' or both strings. 
'In this case, a string is being returned. However, it could just as easily be defined to be Xbasic code that could then be
'evaluated (using evaluate_template()).
'For example, the dependencyList could be defined as:

list = <<%txt%
lastname!ui_msg_box("Change","Lastname changed. New value is: " +lastname)
firstname!ui_msg_box("Change","Firstname changed. New value is: " + firstname)
%txt%

Printer and Screen Capabilities

You can now get information about the capabilities of the screen or printer using the new devicecapabilites object. One of the most useful features of this object is that it allows you to get the dimensions of the printable region of a page. This information is important if you are writing to the printer directly using the ui_printer_draw() command. The following Interactive window session shows how this object is used:

dim pi as gdi::devicecapabilities
 ?pi
 = P Clone() 'Create a copy of an object instance.
 P NewInstance() 'Create a new object instance of the same type.
 l printer_load(C printer_name) 'Get device capabilities for the printer.
 aspect_x = 36
 aspect_xy = 51
 aspect_y = 36
 bitspixel = 32
 clipping_capabilities = "Rectangle"
 color_planes = 1
 color_resolution = 24
 curve_capabilities = ",Chord,Circles,Ellipses,Interiors,Pie,Round-Rect,Styled,Wide,Wide-Styled"
 driver_version = 16384
 line_capabilities = ",Interiors,Marker,Polyline,Polymarker,Styled,Wide,Wide-Styled"
 number_brushes = 4294967295
 number_colors = 4294967295
 number_fonts = 0
 number_pens = 4294967295
 number_reserved_system_palette = 20
 page_height = 0
 page_offset_x = 0
 page_offset_y = 0
 page_width = 0
 pixels_x_per_inch = 96
 pixels_y_per_inch = 96
 polygon_capabilities = ",Interiors,Polygon,Rectangle,Scanline,Styled,Wide,Wide-Styled,Wind-Polygon"
 raster_capabilities = ",BitBLT,Bitmap64,DI-Bitmap,DIBtoDEV,GDI20-Output,StretchBlt,StretchDib"
 screen_height_mm = 310
 screen_height_pixels = 1024
 screen_width_mm = 387
 screen_width_pixels = 1280
 shade_blend_capabilities = ",Const-Alpha,Pixel-Alpha-Blend"
 size_system_palette = 0
 technology = "Raster-Display"
 text_capabilities = ",Stroke-Clip-Precision,character-output-precision,stroke-output-precision,raster-fonts,scrolling,strikeout,underline,vector-fonts"

  

'get a list of printers
 ?ui_printers_get()
 = Text Driver on RTF:
 SagePDFPrinter on LPT1:
 RTF Driver on RTF:
 Ricoh LASER AP2100 PCL on LPT1:
 QuickBooks PDF Converter on LPT1:
 PDF Driver on PDF:
 Microsoft XPS Document Writer on Ne00:
 Microsoft Office Document Image Writer on Ne01:
 HTML Driver on HTM:
 Generic / Text Only on LPT1:
 \\THUNDER\Compaq Laser Printer LNM40 (PCL) on Ne02:
 Alpha Anywhere Printer on RTF:


 printerName = "\\THUNDER\Compaq Laser Printer LNM40 (PCL) on Ne02:"
  

'Now get the capabilities of a specific printer by calling the .printer_load() method of

'the printercapabilities object


 pi.printer_load(printername)

 ?pi 
 = P Clone() 'Create a copy of an object instance.
 P NewInstance() 'Create a new object instance of the same type.
 l printer_load(C printer_name) 'Get device capabilities for the printer.
 aspect_x = 600
 aspect_xy = 849
 aspect_y = 600
 bitspixel = 1
 clipping_capabilities = "Rectangle"
 color_planes = 1
 color_resolution = 0
 curve_capabilities = ",Chord,Circles,Ellipses,Interiors,Pie,Round-Rect,Styled,Wide,Wide-Styled"
 driver_version = 256
 line_capabilities = ",Interiors,Marker,Polyline,Polymarker,Styled,Wide,Wide-Styled"
 number_brushes = 4294967295
 number_colors = 2
 number_fonts = 47
 number_pens = 10
 number_reserved_system_palette = 20
 page_height = 6600
 page_offset_x = 150
 page_offset_y = 150
 page_width = 5100
 pixels_x_per_inch = 600
 pixels_y_per_inch = 600
 polygon_capabilities = ",Interiors,Polygon,Rectangle,Scanline,Styled,Wide,Wide-Styled,Wind-Polygon"
 raster_capabilities = ",BitBLT,Bitmap64,DI-Bitmap,DIBtoDEV,GDI20-Output,StretchBlt,StretchDib"
 screen_height_mm = 268
 screen_height_pixels = 6324
 screen_width_mm = 203
 screen_width_pixels = 4800
 shade_blend_capabilities = ""
 size_system_palette = 2
 technology = "Raster-Printer"
 text_capabilities = ",Stroke-Clip-Precision,character-output-precision,stroke-output-precision,scrolling,strikeout,underline,vector-fonts"
  

 

'find out what the printer's left margin is (in inches)
 printer_leftMargin_inInches = pi.page_offset_x/pi.pixels_x_per_inch
 ?printer_leftMargin_inInches
 = 0.25

Determine the Version Number of an EXE or DLL

UTIL::ExecutableVersion is a new class that an be used to extract the version information from an executable or DLL. Version information is actually a collection (keyed on language and codepage). Typically there will be a single entry, but you will need to use a subscript to get at the values.

  • Usage

    Dim XV as Util::ExecutableVersion
     Dim FileName as C = ��
     If .NOT. XV.Load(FileName) then
         ui_msg_box(�Error loading version data for file � + FileName, XV.CallResult.Text + crlf() + XV.CallResult.NativeText)
     else
         showvar(XV)
     End if
  • Example Interactive Window Session

    dim x as util::executableversion
     ?x.load("c:\foo.dll")
     = .F.
      
     ?x.callresult.text 
     = "DOS error"
      
     ?x.callresult 
     = Canceled = .F.
     Code = 2000
     Error = .T.
     NativeCode = 1812
     NativeText = The specified image file did not contain a resource section.
      
     Success = .F.
     Text = "DOS error"
      
     ?x.load("c:\windows\system32\fqqb32.dll")
     = .T.
      
     ?x
     = P Clone() 'Create a copy of an object instance.
     N EntryNumber(Name as C) 'Get the index of a Entry from the name.
     L Load(C FileName) 'Retrieve version information for the file requested.
     P NewInstance() 'Create a new object instance of the same type.
     +CallResult.
     +Entry.
     FileName = "c:\windows\system32\fqqb32.dll"
      
      
     ?x.entry[1] 
     = P Clone() 'Create a copy of an object instance.
     P NewInstance() 'Create a new object instance of the same type.
     CodePage = 1252
     Comments = ""
     CompanyName = "FLEXquarters.com LLC"
     FileDescription = "FLEXquarters.com LLC QuickBooks ODBC Driver"
     FileVersion = " 5.00.00.077"
     InternalName = "FQQB32"
     Language = 1033
     LegalCopyright = "Copyright � FLEXquarters.com LLC 1998-2004"
     LegalTrademarks = "QODBC(TM) is a trademark of FLEXquarters.com LLC"
     Name = "040904e4"
     OriginalFileName = "FQQB32.dll"
     PrivateBuild = ""
     ProductName = "FLEXquarters.com LLC QODBC"
     ProductVersion = " 5.00.00.077"
     SpecialBuild = ""

Layouts Implement .Eval() Method

Layouts (e.g. Forms, Reports, etc.) implement an .Eval() method to evaluate an expression in the context of the layout. For example, assume that you had this expression: lastname.text and you wanted to evaluate it in the context of a form. Previously, you would have to do this:

f = form.view("Edit_Customer_Info")
         ?eval("lastname.text",f.name() + ".this")
         = "Graham"

Now, you can simply do this:

f = form.view("Edit_Customer_Info")
         ?f.eval("lastname.text"),

        = "Graham"

CSS Class

The CSS class makes it easy to manipulate CSS stylesheets using Xbasic. The following Interactive Window session demonstrates the functionality. Assume that you have a file on disk with the following trivial CSS:

a  {font-family: Alba; }
 abbr { font-family: Arial Narrow; }

Now, from the Interactive window:

DIM txt as c 

txt = get_from_file("c:\mycsstxt.txt")

DIM ss as css::stylesheet

ss.FromString(txt)
 ? ss.toString()
 = a {
 font-family: : Alba;
 }
 abbr {
 font-family: : Arial Narrow;
 }

 ? ss.item[1].name
 = "a"
 ? ss.item[2].name
 = "abbr"
 ? ss.item[2].css.tostring()
 = font-family: : Arial Narrow;
 ? ss.Selectors()
 = a
 abbr

 

ss.item[1].css.border_color = "Red"

 ?ss.ToString()
 = a {
 font-family: Alba;
 border-color: Red;
 }
 abbr {
 font-family: Arial Narrow;
 }

Table Cursors

.get_cursor() Method - Allows you to get a 'cursor' for an open table. A 'cursor' is just a pointer to an open table that can be navigated independently of the base table. When you move the pointer in a cursor, you do not change the record that the base table pointer is pointing to. Syntax: tCursor = .get_cursor() Example:

dim t as p 
dim tc1 as p 
dim tc2 as p 
t = table.open("customer")
tc1 = t.get_cursor()
tc2 = t.get_cursor()

'The cursors can each have their own order. Has no impact on the base table
tc1.order("Firstname")
tc2.order("Lastname")
?t.recno()
=1
?tc1.recno()
=23
?tc2.recno()
=34
tc1.fetch_next()
?tc1.recno()
= 56
'Notice how we have moved the record pointer in the first cursor, but
'it has had no effect on the base table (which still points to record 1)
?t.recno()
= 1

The primary motivation for this method is to allow you to use Xbasic to get a pointer to the table that an Embedded Browse is based on, and then to safely loop over the records in that table and manipulate the data in that table. If your Xbasic loop used the table pointer itself, rather than a cursor, your script may not work reliably. This is because you have no control over events that cause the Browse to repaint, and when the Browse repaints, it might move the record pointer. Example. The 'Invoice' Form in AlphaSports is based on the Invoice.set. This Form has an embedded browse that is based on the 'invoice_items' table. You want to write a script that will loop through all of the records for the current invoice, increasing the quantity by one. Here is code that could be inserted in a button on the Invoice form:

dim tbl as p 
 dim t as p 

'get a pointer to the SAME table instance that the embedded browse is based on.

'Note: Could also have done: tbl = Browse1.table_get()
 tbl = table.get("invoice_items")

'Get a cursor on the table instance
 t = tbl.get_cursor()

 

t.fetch_first()
 while .not. t.fetch_eof()
     t.change_begin()
     t.quantity = t.quantity + 1 
     t.change_end(.t.)
     t.fetch_next()
 end while

You might ask, "Why do I have to go to the trouble of getting a cursor? Why can't I just write code that uses the same table pointer that the Embedded Browse uses?". The reason is that the script shown below will not work reliably. The reason is this: You have no control over when the Browse will repaint. When the Browse repaints it affects the record that the table pointer is positioned on. So, if your script is using the same table pointer that the Embedded Browse is using, then the code in your loop that positions the record pointer will be impacted every time the Browse repaints during the course of your loop. The Browse will be repainted every time the message box is dismissed.

'This code is NOT safe (could result in Browse repaint errors)

dim tbl as p 
 tbl = table.get("invoice_items")

 

tbl.fetch_first()
 dim count as n = 0 
 while .not. tbl.fetch_eof()

    count = count + 1 

    ui_msg_box("Notice","Editing item number: " + count)
     tbl.change_begin()
     tbl.quantity = t.quantity + 1 
     tbl.change_end(.t.)
     tbl.fetch_next()
 end while